﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Styx.CommonBot.Routines;
using Styx;
using Styx.TreeSharp;
using Styx.WoWInternals.WoWObjects;
using Styx.WoWInternals;
using Styx.CommonBot;
using Action = Styx.TreeSharp.Action;
using Styx.Common;
using System.Windows.Media;
using System.Globalization;
using Styx.Pathing;

namespace Beast_Mastery
{
    public class Main : CombatRoutine
    {
        public override string Name { get { return "Beast Mastery"; } }
        public override WoWClass Class { get { return WoWClass.Hunter; } }
        LocalPlayer Me { get { return StyxWoW.Me; } }

        private Composite _preCombatBuffs, _combatBuffs, _Combat;
        public override Composite PreCombatBuffBehavior { get { return _preCombatBuffs; } }
        public override Composite CombatBuffBehavior { get { return _combatBuffs; } }
        public override Composite CombatBehavior { get { return _Combat; } }
        
        #region Initialize
        public override void Initialize()
        {
            _preCombatBuffs = createPreCombatBuffs();
            _combatBuffs = createCombatBuffs();
            _Combat = createCombat();
        }
        #endregion

        #region preCombatBuffs
        Composite createPreCombatBuffs()
        {
            return
                new PrioritySelector(
                    //0 	0.00 	flask,type=spring_blossoms
                    //1 	0.00 	food,type=sea_mist_rice_noodles
                    //aspect_of_the_hawk
                    Cast(109260, ret => !Me.HasAura(109260))
                    //3 	0.00 	hunters_mark,if=target.time_to_die>=21&!debuff.ranged_vulnerability.up
                    //4 	0.00 	summon_pet
                    //5 	0.00 	trueshot_aura
                    //7 	0.00 	virmens_bite_potion
                    );
        }
        #endregion

        #region combatBuffs
        Composite createCombatBuffs()
        {
            return
                new PrioritySelector(
                    //0 	0.00 	flask,type=spring_blossoms
                    //aspect_of_the_hawk
                    Cast(109260, ret => !Me.HasAura(109260))
                    //3 	0.00 	hunters_mark,if=target.time_to_die>=21&!debuff.ranged_vulnerability.up
                    //4 	0.00 	summon_pet
                    //5 	0.00 	trueshot_aura
                    //7 	0.00 	virmens_bite_potion
                    );
        }
        #endregion

        #region Combat
        Composite createCombat()
        {
            return
                new Decorator(ret => !Me.Mounted,
                    new PrioritySelector(
                        //8 	1.00 	virmens_bite_potion,if=buff.bloodlust.react|target.time_to_die<=60
                        //9 	1.00 	auto_shot
                        //explosive_trap,if=target.adds>0
                        new Action(delegate
                            {
                                if (Me.CurrentTarget != null && nearbyAttackableUnits(Me.CurrentTarget.Location, 10).Count() > 0 && Me.HasAura(77769))
                                {
                                    SpellManager.Cast(82939);
                                    SpellManager.ClickRemoteLocation(Me.CurrentTarget.Location);
                                }
                                return RunStatus.Failure;
                            }),
                        //focus_fire,five_stacks=1
                        Cast(82692, ret => Me.CurrentTarget != null && frenzyStack == 5),
                        //serpent_sting,if=!ticking
                        Cast(1978, ret => Me.CurrentTarget != null && !Me.CurrentTarget.HasAura(118253) && lastSpellCast != 1978),
                        //D 	4.29 	blood_fury
                        //E 	0.00 	fervor,if=enabled&!ticking&focus<=65
                        //bestial_wrath,if=focus>60&!buff.beast_within.up
                        Cast(19574, ret => Me.CurrentTarget != null && playerFocus > 60 && !Me.HasAura(34471) && (Me.CurrentTarget.IsBoss || Me.CurrentTarget.IsPlayer)),
                        //multi_shot,if=target.adds>5
                        Cast(2643, ret => Me.CurrentTarget != null && nearbyAttackableUnits(Me.CurrentTarget.Location, 8).Count() > 5),
                        //cobra_shot,if=target.adds>5
                        channelSpell(56641, ret => Me.CurrentTarget != null && nearbyAttackableUnits(Me.Location, 8).Count() > 5),
                        //rapid_fire,if=!buff.rapid_fire.up
                        Cast(3045, ret => Me.CurrentTarget != null && !Me.HasAura(3045) && (Me.CurrentTarget.IsBoss || Me.CurrentTarget.IsPlayer)),
                        //J 	2.00 	stampede,if=buff.rapid_fire.up|buff.bloodlust.react|target.time_to_die<=25
                        //kill_shot
                        Cast(53351, ret => Me.CurrentTarget != null && Me.CurrentTarget.HealthPercent <= 20),
                        //kill_command
                        Cast(34026, ret => Me.CurrentTarget != null && Me.Pet.Location.Distance(Me.CurrentTarget.Location) <= 25),
                        //a_murder_of_crows,if=enabled&!ticking
                        Cast(131894, ret => Me.CurrentTarget != null && !Me.CurrentTarget.HasAura(131894)),
                        //N 	29.53 	glaive_toss,if=enabled
                        //O 	0.00 	lynx_rush,if=enabled&!dot.lynx_rush.ticking
                        //dire_beast,if=enabled&focus<=90
                        Cast(120679, ret => Me.CurrentTarget != null && playerFocus <= 90),
                        //Q 	0.00 	barrage,if=enabled
                        //R 	0.00 	powershot,if=enabled
                        //S 	0.00 	blink_strike,if=enabled
                        //readiness,wait_for_rapid_fire=1
                        Cast(23989, ret => Me.HasAura(3045) && (Me.CurrentTarget.IsBoss || Me.CurrentTarget.IsPlayer)),
                        //U     0.00    arcane_shot,if=buff.thrill_of_the_hunt.react
                        //focus_fire,five_stacks=1,if=!ticking&!buff.beast_within.up
                        Cast(82692, ret => Me.CurrentTarget != null && frenzyStack == 5 && !Me.HasAura(82692) && !Me.HasAura(34471)),
                        //cobra_shot,if=dot.serpent_sting.remains<6
                        channelSpell(56641, ret => Me.CurrentTarget != null && !cobraSting),
                        //arcane_shot,if=focus>=61|buff.beast_within.up
                        Cast(3044, ret => Me.CurrentTarget != null && (playerFocus >= 61 || Me.HasAura(34471))),
                        //cobra_shot
                        channelSpell(56641, ret => Me.CurrentTarget != null)
                        ));
        }
        #endregion

        #region Methods
        #region playerFocus
        double playerFocus
        {
            get
            {
                try
                {
                    using (StyxWoW.Memory.AcquireFrame())
                    {
                        return Styx.WoWInternals.Lua.GetReturnVal<int>("return UnitPower(\"player\");", 0);
                    }
                }
                catch { return 0; }
            }
        }
        #endregion

        #region spellCast
        delegate T Selection<out T>(object context);
        int lastSpellCast;
        Composite Cast(int spell, Selection<bool> reqs = null)
        {
            return
                new Decorator(ret => ((reqs != null && reqs(ret)) || (reqs == null)) && SpellManager.CanCast(spell),
                    new Sequence(
                        new Action(ret => SpellManager.Cast(spell)),
                        new Action(ret => lastSpellCast = spell),
                        new Action(ret => Logging.Write(Colors.Orange, "Casting:" + spell + "@" + Me.CurrentTarget.Name + ""))));
        }
        bool isChanneling { get { return Me.ChanneledCastingSpellId != 0 && Me.IsChanneling; } }
        Composite channelSpell(int spell, Selection<bool> reqs = null)
        {
            return
                new Decorator(ret => ((reqs != null && reqs(ret)) || (reqs == null)) && SpellManager.CanCast(spell) && !isChanneling,
                    new Sequence(
                        new Action(ret => SpellManager.Cast(spell)),
                        new Action(ret => Logging.Write(Colors.DarkOrange, "Casting:" + spell + "@" + Me.CurrentTarget.Name + ""))));
        }
        #endregion

        #region Auras
        bool cobraSting
        {
            get
            {
                if (!Me.GotTarget)
                    return false;
                WoWAura SerpentSting = Me.CurrentTarget.GetAllAuras().FirstOrDefault(u => u.CreatorGuid == Me.Guid && u.SpellId == 118253);
                return SerpentSting != null && SerpentSting.TimeLeft >= TimeSpan.FromSeconds(6);
            }
        }
        uint frenzyStack
        {
            get
            {
                WoWAura result = Me.GetAllAuras().FirstOrDefault(a => a.SpellId == 19615 && a.StackCount > 0);
                if (result != null) return result.StackCount;
                return 0;
            }
        }
        #endregion

        #region AoE
        IEnumerable<WoWUnit> unfriendlyUnits
        {
            get { return ObjectManager.GetObjectsOfType<WoWUnit>(true, false).Where(u => !u.IsDead && u.CanSelect && u.Attackable && !u.IsFriendly && !u.IsNonCombatPet && !u.IsCritter); }
        }
        IEnumerable<WoWUnit> nearbyAttackableUnits(WoWPoint fromLocation, double radius)
        {
            var hostile = unfriendlyUnits;
            var maxDistance = radius * radius;
            return hostile.Where(x => x.Location.DistanceSqr(fromLocation) < maxDistance);
        }
        #endregion

        #region Trinkets
        bool canUseEquippedItem(WoWItem item)
        {
            string itemSpell = Lua.GetReturnVal<string>("return GetItemSpell(" + item.Entry + ")", 0);
            if (string.IsNullOrEmpty(itemSpell))
                return false;
            return item.Usable && item.Cooldown <= 0;
        }
        void useTrinkets()
        {
            var firstTrinket = Me.Inventory.Equipped.Trinket1;
            var secondTrinket = Me.Inventory.Equipped.Trinket2;
            if (firstTrinket != null && canUseEquippedItem(firstTrinket))
                firstTrinket.Use();
            if (secondTrinket != null && canUseEquippedItem(secondTrinket))
                secondTrinket.Use();
        }
        #endregion
        #endregion
    }
}
